package com.tsfyun.scm.service.impl.finance;

import cn.hutool.core.util.RandomUtil;
import com.alibaba.excel.EasyExcel;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.google.common.collect.ImmutableMap;
import com.tsfyun.common.base.dto.*;
import com.tsfyun.common.base.enums.MessageNodeEnum;
import com.tsfyun.common.base.enums.WxMessageDefineTemplate;
import com.tsfyun.common.base.enums.domain.DomainOprationEnum;
import com.tsfyun.common.base.enums.SerialNumberTypeEnum;
import com.tsfyun.common.base.enums.domain.DomainTypeEnum;
import com.tsfyun.common.base.enums.domain.ReceiptAccountStatusEnum;
import com.tsfyun.common.base.enums.finance.ReceivingModeEnum;
import com.tsfyun.common.base.enums.finance.TransactionCategoryEnum;
import com.tsfyun.common.base.enums.finance.TransactionTypeEnum;
import com.tsfyun.common.base.exception.ServiceException;
import com.tsfyun.common.base.help.excel.CustomCellStyleHandler;
import com.tsfyun.common.base.help.excel.CustomCellWriteHandler;
import com.tsfyun.common.base.help.excel.LocalDateTimeConverter;
import com.tsfyun.common.base.support.DomainStatus;
import com.tsfyun.common.base.util.LocalDateTimeUtils;
import com.tsfyun.common.base.util.StringUtils;
import com.tsfyun.common.base.util.TsfPreconditions;
import com.tsfyun.scm.dto.finance.ReceiptAccountDTO;
import com.tsfyun.scm.dto.finance.ReceiptAccountQTO;
import com.tsfyun.scm.dto.support.TaskNoticeContentDTO;
import com.tsfyun.scm.entity.base.Currency;
import com.tsfyun.scm.entity.customer.Customer;
import com.tsfyun.scm.entity.finance.ReceiptAccount;
import com.tsfyun.scm.entity.user.Person;
import com.tsfyun.scm.excel.output.ReceiptAccountOutExcel;
import com.tsfyun.scm.mapper.finance.ReceiptAccountMapper;
import com.tsfyun.scm.service.base.ICurrencyService;
import com.tsfyun.scm.service.common.ICommonService;
import com.tsfyun.scm.service.customer.ICustomerService;
import com.tsfyun.scm.service.file.IExportFileService;
import com.tsfyun.scm.service.finance.IReceiptAccountMemberService;
import com.tsfyun.scm.service.finance.IReceiptAccountService;
import com.tsfyun.common.base.extension.ServiceImpl;
import com.tsfyun.scm.service.finance.ITransactionFlowService;
import com.tsfyun.scm.service.finance.IWriteOffService;
import com.tsfyun.scm.service.support.ITaskNoticeContentService;
import com.tsfyun.scm.service.system.ISerialNumberService;
import com.tsfyun.scm.service.system.IStatusHistoryService;
import com.tsfyun.scm.service.third.IShortMessageService;
import com.tsfyun.scm.service.third.IWxMessageService;
import com.tsfyun.scm.service.user.IPersonService;
import com.tsfyun.scm.stream.send.ScmProcessor;
import com.tsfyun.scm.util.CheckUtil;
import com.tsfyun.scm.util.ExportUtil;
import com.tsfyun.scm.vo.finance.ReceiptAccountDetailPlusVO;
import com.tsfyun.scm.vo.finance.ReceiptAccountMemberVO;
import com.tsfyun.scm.vo.finance.ReceiptAccountVO;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.io.File;
import java.math.BigDecimal;
import java.util.*;

/**
 * <p>
 * 收款单 服务实现类
 * </p>
 *
 * @author z.mark
 * @since 2020-04-15
 */
@RefreshScope
@Service
public class ReceiptAccountServiceImpl extends ServiceImpl<ReceiptAccount> implements IReceiptAccountService {

    @Autowired
    private ReceiptAccountMapper receiptAccountMapper;
    @Autowired
    private ICurrencyService currencyService;
    @Autowired
    private IStatusHistoryService statusHistoryService;
    @Autowired
    private ISerialNumberService serialNumberService;
    @Autowired
    private ICustomerService customerService;
    @Autowired
    private ITaskNoticeContentService taskNoticeContentService;
    @Autowired
    private ICommonService commonService;
    @Autowired
    private ITransactionFlowService transactionFlowService;
    @Autowired
    private ScmProcessor processor;
    @Autowired
    private IReceiptAccountMemberService receiptAccountMemberService;
    @Value("${file.directory}")
    private String filePath;
    @Autowired
    private IExportFileService exportFileService;
    @Autowired
    private IWriteOffService writeOffService;
    @Autowired
    private IPersonService personService;
    @Autowired
    private IShortMessageService shortMessageService;
    @Autowired
    private IWxMessageService wxMessageService;

    @Transactional(rollbackFor = Exception.class)
    @Override
    public Long register(ReceiptAccountDTO dto) {
        ReceiptAccount receiptAccount = beanMapper.map(dto,ReceiptAccount.class);
        Customer customer = customerService.findByNameWithRight(dto.getCustomerName());
        //币制编码默认为人民币
        receiptAccount.setCurrencyId("CNY");
        Currency currency = currencyService.findById(receiptAccount.getCurrencyId());
        receiptAccount.setCurrencyName(currency.getName());
        receiptAccount.setCustomerId(customer.getId());
        //待确定
        ReceiptAccountStatusEnum statusEnum = ReceiptAccountStatusEnum.WAIT_CONFIRM;
        receiptAccount.setStatusId(statusEnum.getCode());
        //生成系统单号
        receiptAccount.setDocNo(serialNumberService.generateDocNo(SerialNumberTypeEnum.IMP_RECEIPT_ACCOUNT));
        try {
            super.saveNonNull(receiptAccount);
        } catch (DuplicateKeyException e) {
            throw new ServiceException("系统单号已经被占用，请稍后再试");
        }
        //记录历史状态
        statusHistoryService.saveHistory(DomainOprationEnum.RECEIPT_REGISTER,
                receiptAccount.getId().toString(), ReceiptAccount.class.getName(),
                statusEnum.getCode(),statusEnum.getName(),"收款登记");

        //发送任务通知
        TaskNoticeContentDTO taskNoticeContentDTO = new TaskNoticeContentDTO();
        taskNoticeContentDTO.setDocumentType(DomainTypeEnum.RECEIPTACCOUNT.getCode());
        taskNoticeContentDTO.setDocumentId(receiptAccount.getId().toString());
        taskNoticeContentDTO.setCustomerId(receiptAccount.getCustomerId());
        taskNoticeContentDTO.setOperationCode(DomainOprationEnum.RECEIPT_CONFIRM.getCode());
        taskNoticeContentDTO.setContent(String.format("客户【%s】有一单收款【%s】需要您确认。", customer.getName(),receiptAccount.getDocNo()));
        taskNoticeContentDTO.setQueryParamsMap(ImmutableMap.of("docNo",receiptAccount.getDocNo()));
        taskNoticeContentService.add(taskNoticeContentDTO);
        return receiptAccount.getId();
    }

    @Override
    public PageInfo<ReceiptAccountVO> pageList(ReceiptAccountQTO qto) {
        Map<String,Object> params = beanMapper.map(qto, Map.class);
        PageHelper.startPage(qto.getPage(),qto.getLimit());
        List<ReceiptAccountVO> list = receiptAccountMapper.list(params);
        return new PageInfo<>(list);
    }

    @Override
    public ReceiptAccountVO detail(Long id) {
        return detail(id,null);
    }

    @Override
    public ReceiptAccountVO detail(Long id, String operation) {
        ReceiptAccountVO receiptAccount = receiptAccountMapper.detail(id);
        TsfPreconditions.checkArgument(Objects.nonNull(receiptAccount),new ServiceException("收款数据不存在"));
        //验证状态是否可以执行操作
        DomainStatus.getInstance().check(DomainOprationEnum.of(operation), receiptAccount.getStatusId());
        return receiptAccount;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
//    @GlobalTransactional(rollbackFor = Exception.class)
    public void confirm(TaskDTO dto) {
        //修改后单据状态
        ReceiptAccountStatusEnum nowStatusEnum = ReceiptAccountStatusEnum.of(dto.getNewStatusCode());
        ReceiptAccount receiptAccount = super.getById(dto.getDocumentId());
        TransactionFlowDTO param = new TransactionFlowDTO();
        switch (nowStatusEnum){
            case CONFIRMED://已经确认
                //写入交易流水
                param.setCustomerId(receiptAccount.getCustomerId());
                param.setTransactionType(TransactionTypeEnum.RECEIVABLES.getCode());
                param.setTransactionCategory(TransactionCategoryEnum.INCOME.getCode());
                param.setTransactionNo(receiptAccount.getDocNo());
                param.setAmount(receiptAccount.getAccountValue());
                param.setMemo(receiptAccount.getMemo());
                transactionFlowService.recordTransactionFlow(param);

                //消息通知核销
                CostWriteOffDTO cwdto = new CostWriteOffDTO();
                cwdto.setTenant("");
                cwdto.setCustomerId(receiptAccount.getCustomerId());
                processor.sendCostWriteOff(cwdto);

                //发送短信通知客户
                LinkedHashMap<String,String> paramsMap = new LinkedHashMap<>();
                paramsMap.put("amount", StringUtils.formatCurrency(receiptAccount.getAccountValue()));
                SmsNoticeMessageDTO smsNoticeMessageDTO = new SmsNoticeMessageDTO();
                smsNoticeMessageDTO.setMessageNodeEnum(MessageNodeEnum.CUSTOMER_PAY);
                Person person = personService.getPhoneByCustomerId(receiptAccount.getCustomerId());
                smsNoticeMessageDTO.setPhoneNo(Objects.nonNull(person) ? person.getPhone() : "");
                smsNoticeMessageDTO.setParams(paramsMap);
                shortMessageService.sendNoticeMessage(smsNoticeMessageDTO);

                //发送微信通知
                WxNoticeMessageDTO wxNoticeMessageDTO = new WxNoticeMessageDTO();
                wxNoticeMessageDTO.setWxgzhOpenid(Objects.nonNull(person) ? person.getWxgzhOpenid() : "");
                wxNoticeMessageDTO.setWxMessageDefineTemplate(WxMessageDefineTemplate.CUSTOMER_PAY);
                wxNoticeMessageDTO.setIsNavToMiniprogram(Boolean.TRUE);
                wxNoticeMessageDTO.setMiniprogram(new WxMiniProgram("pages/finance/receipt/list?currentStatus=2"));
                LinkedHashMap<String, WxNoticeAttr> params = new LinkedHashMap<>();
                params.put("first",new WxNoticeAttr("付款确认通知"));
                params.put("keyword1",new WxNoticeAttr(StringUtils.formatCurrency(receiptAccount.getAccountValue())));
                params.put("keyword2",new WxNoticeAttr(ReceivingModeEnum.of(receiptAccount.getReceivingMode()).getName(),"#9C9C9C"));
                params.put("keyword3",new WxNoticeAttr(LocalDateTimeUtils.formatTime(receiptAccount.getAccountDate(),"yyyy-MM-dd HH:mm"),"#9C9C9C"));
                params.put("keyword4",new WxNoticeAttr(receiptAccount.getDocNo(),"#9C9C9C"));
                params.put("remark",new WxNoticeAttr("点击查看我的所有已付款","#9C9C9C"));
                wxNoticeMessageDTO.setParams(params);
                wxMessageService.sendWxNotice(wxNoticeMessageDTO);
                break;
            case INVALID://已作废
                //收款已确认
                if(ReceiptAccountStatusEnum.CONFIRMED.getCode().equals(receiptAccount.getStatusId())){
                    //还原核销
                    writeOffService.cancelwriteOffByReceiptAccount(receiptAccount);
                    //还原交易流水
                    param.setCustomerId(receiptAccount.getCustomerId());
                    param.setTransactionType(TransactionTypeEnum.RECEIVABLES.getCode());
                    param.setTransactionCategory(TransactionCategoryEnum.OUTCOME.getCode());
                    param.setTransactionNo(receiptAccount.getDocNo());
                    param.setAmount(receiptAccount.getAccountValue());
                    param.setMemo("作废收款");
                    transactionFlowService.recordTransactionFlow(param);

//                    //发送微信通知
//                    Person tempPerson = personService.getPhoneByCustomerId(receiptAccount.getCustomerId());
//                    Customer customer = customerService.getById(receiptAccount.getCustomerId());
//                    if(Objects.nonNull(tempPerson) && Objects.nonNull(customer)) {
//                        WxNoticeMessageDTO tempWxNoticeMessageDTO = new WxNoticeMessageDTO();
//                        tempWxNoticeMessageDTO.setWxgzhOpenid(Objects.nonNull(tempPerson) ? tempPerson.getWxgzhOpenid() : "");
//                        tempWxNoticeMessageDTO.setWxMessageDefineTemplate(WxMessageDefineTemplate.INVALID_NOTICE);
//                        tempWxNoticeMessageDTO.setIsNavToMiniprogram(Boolean.TRUE);
//                        tempWxNoticeMessageDTO.setMiniprogram(new WxMiniProgram("pages/finance/receipt/list?docNo=" + receiptAccount.getDocNo()));
//                        LinkedHashMap<String, WxNoticeAttr> tempParams = new LinkedHashMap<>();
//                        tempParams.put("first",new WxNoticeAttr("付款作废通知"));
//                        String invalidContent = StrUtil.format( "尊敬的{}，您有一笔付款单号为{}，付款金额为{}的付款单作废",customer.getName(),receiptAccount.getDocNo(),StringUtils.formatCurrency(receiptAccount.getAccountValue()));
//                        tempParams.put("keyword1",new WxNoticeAttr(invalidContent,"#9C9C9C"));
//                        tempParams.put("keyword2",new WxNoticeAttr(dto.getMemo()));
//                        tempParams.put("remark",new WxNoticeAttr("点击查看付款详情","#9C9C9C"));
//                        tempWxNoticeMessageDTO.setParams(tempParams);
//                        wxMessageService.sendWxNotice(tempWxNoticeMessageDTO);
//                    }
                }
                //删除任务通知
                clearTaskNotice(receiptAccount.getId());
                break;
            default:
                throw new ServiceException("当前状态不允许此操作");
        }
        //修改状态
        commonService.changeDocumentStatus(dto);
    }

    @Override
    public BigDecimal obtainCustomerReceipts(Long customerId) {
        return receiptAccountMapper.obtainCustomerReceipts(customerId);
    }

    @Override
    public List<ReceiptAccount> canWriteOffList(Long customerId){
        return receiptAccountMapper.canWriteOffList(customerId);
    }

    @Override
    public ReceiptAccountDetailPlusVO plusDetail(Long id) {
        ReceiptAccountVO receiptAccount = detail(id);
        Optional.ofNullable(receiptAccount).orElseThrow(()->new ServiceException("收款单信息不存在"));
        //获取收款单明细信息
        List<ReceiptAccountMemberVO> members = receiptAccountMemberService.getVoByReceiptAccountId(receiptAccount.getId());
        return new ReceiptAccountDetailPlusVO(receiptAccount,members);
    }

    @Override
    public Long exportExcel(ReceiptAccountQTO qto) {
        //根据条件获取导出数据，限制开始时间和结束时间只能为1年，防止数据过大导出内存溢出
        CheckUtil.checkTime(qto.getAccountDateStart(),qto.getAccountDateEnd());
        //如果参数二串一，则赋值其中一个日期字段
        if(Objects.nonNull(qto.getAccountDateStart()) && Objects.isNull(qto.getAccountDateEnd())) {
            qto.setAccountDateEnd(qto.getAccountDateStart().plusYears(1));
        }
        if(Objects.isNull(qto.getAccountDateStart()) && Objects.nonNull(qto.getAccountDateEnd())) {
            qto.setAccountDateStart(qto.getAccountDateStart().plusYears(-1));
        }
        //获取需要导出的数据
        Map<String,Object> params = beanMapper.map(qto, Map.class);
        List<ReceiptAccountVO> list = receiptAccountMapper.list(params);
        List<ReceiptAccountOutExcel> dataList = beanMapper.mapAsList(list, ReceiptAccountOutExcel.class);

        //导出excel
        String[] titles = {"状态","收款单号","收款时间","结算单位","币制","收款金额","核销金额","收款方式","收款行","制单人"};
        List<List<String>>  headList = ExportUtil.headListNoIndex(titles);

        String path = filePath + "temp/scm/receiptExcel/";
        if(!new File(path).exists()) {
            new File(path).mkdirs();
        }
        String sysFileName = String.format("%s_%s.xlsx", RandomUtil.randomString(4), LocalDateTimeUtils.formatNow("yyyyMMddHHmmssSSS"));
        EasyExcel.write(path + sysFileName,ReceiptAccountOutExcel.class).registerConverter(new LocalDateTimeConverter())
                .registerWriteHandler(new CustomCellWriteHandler()).registerWriteHandler(new CustomCellStyleHandler()).head(headList).sheet("收款列表").doWrite(dataList);

        //保存导出信息
        ExportFileDTO exportFileDTO = new ExportFileDTO();
        exportFileDTO.setPath(path + sysFileName);
        exportFileDTO.setFileName(sysFileName);
        exportFileDTO.setOnce(Boolean.TRUE);
        exportFileDTO.setOperator("系统自动生成");
        return exportFileService.save(exportFileDTO);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void cancelWriteValue(Long id, BigDecimal writeValue) {
        receiptAccountMapper.cancelWriteValue(id,writeValue);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void writeValue(Long id, BigDecimal writeValue) {
        receiptAccountMapper.writeValue(id,writeValue);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void removeAllById(Long id) {
        //已作废的数据才允许删除
        ReceiptAccount receiptAccount = super.getById(id);
        if(Objects.isNull(receiptAccount) || !Objects.equals(ReceiptAccountStatusEnum.INVALID,ReceiptAccountStatusEnum.of(receiptAccount.getStatusId()))){
            throw new ServiceException("已作废的单据才允许删除");
        }
        //删除历史状态
        statusHistoryService.removeHistory(id.toString(), ReceiptAccount.class.getName());
        //删除单据
        super.removeById(id);
        //清空任务
        clearTaskNotice(id);
    }

    //清空任务通知
    public void clearTaskNotice(Long id){
        taskNoticeContentService.deleteTaskNotice(DomainTypeEnum.RECEIPTACCOUNT.getCode(), id, "");
    }
}
